package com.albd.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

import android.util.Log;


public class ShellCommand
{

	private static final String SU = "su";

	private static final String SH = "sh";

	private static final String TEST_SHELL = "id";

	private Process mProcess = null;

	private BufferedReader inBuffer = null;

	private OutputStream out = null;

	private Integer exitCode = null;

	private Thread mShellDaemonThread = new Thread()
	{
		@Override public void run()
		{
			while (exitCode == null && mProcess != null && mShellDaemonThread != null)
			{
				try
				{
					exitCode = mProcess.waitFor();
				}
				catch (InterruptedException e)
				{}
			}
			if(mShellDaemonThread != null)
			{
				closeShell();
			}
		}
	};

	public ShellCommand()
	{
		this(false);
	}

	public static String getProcessName(int pid)
	{
		String processName = null;
		try
		{
			processName = FileUtil.readFile("/proc/" + pid + "/cmdline", '\0');
		}
		catch (Exception e)
		{
		}
		return processName;
	}
	
	public static int PidOf(String processName)
	{		
		File procdir = new File("/proc");
		File[] fs = procdir.listFiles();
		for(int i = 0; i < fs.length; i++)
		{
			int pid = -1;
			try{ pid = Integer.parseInt(fs[i].getName()); }
			catch (Exception e)
			{
				pid = -1;
			}
			if(pid > 0)
			{
				if(processName.equals(getProcessName(pid)))
					return pid;								
			}
		}
		return -1;
	}

	public ShellCommand(boolean root)
	{
		String initCommand = root? SU : SH;
		try
		{
			mProcess = Runtime.getRuntime().exec(initCommand);
			try
			{
				Thread.sleep(100);
			}
			catch (Exception e)
			
			{}
			try
			{
				exitCode = mProcess.exitValue();
			}
			catch (IllegalThreadStateException e)
			{}
			
			if(exitCode == null)
			{
				mShellDaemonThread.start();
				inBuffer = getBufferedReader(mProcess.getInputStream());
				out = mProcess.getOutputStream();
//				testShell();
			}
			else
			{
				throw new NotFindShellProcess();
			}
		}
		catch (Exception e)
		{
			closeShell();
		}
	}

	public void testShell()
	{
		try
		{
			String log = runCommand(TEST_SHELL);
			if(log != null)
			{
				Log.w("ShellCommand", log);
			}
		}
		catch (NotFindShellProcess e)
		{
			throw e;
		}
		catch (RuntimeException e)
		{
			InputStream err = mProcess.getErrorStream();
			String errorString = null;
			try
			{
				if(err.available() > 0)
				{
					BufferedReader errBuffer = getBufferedReader(err);
					errorString = readRequset(errBuffer);
				}
			}
			catch (IOException e1)
			{}
			if(errorString != null)
			{
				throw new RuntimeException(errorString, e);
			}
			else
			{
				throw e;
			}

		}
	}

	public Integer getExitCode()
	{
		return exitCode;
	}

	public void closeShell()
	{
		if(mProcess != null)
		{
			mProcess.destroy();
			mProcess = null;
		}

		if(inBuffer != null)
		{
			try
			{
				inBuffer.close();
			}
			catch (IOException e)
			{}
			inBuffer = null;
		}

		if(out != null)
		{
			try
			{
				out.close();
			}
			catch (IOException e)
			{}
			out = null;
		}

		exitCode = Integer.MIN_VALUE;

		if(mShellDaemonThread != null)
		{
			mShellDaemonThread.interrupt();
			mShellDaemonThread = null;
		}
	}

	public String runCommandOrScript(String command)
	{
		String[] script = command.split("\n");
		if(script.length > 1)
		{
			return runScript(script);
		}
		else
		{
			return runCommand(command);
		}
	}

	public String runCommand(String command)
	{
		return runCommand(command, null);
	}
	
//	public String runCommandAndLog(String command)
//	{
//		return runCommand(command, "/sdcard/shell.txt");
//	}
	
	public String runCommand(String command, String outFile)
	{
		Log.i("Shell", "<< " + command);		
		if(exitCode != null)
		{
			closeShell();
			throw new NotFindShellProcess();
		}
		String request = null;
		synchronized (mProcess)
		{
			try
			{
				writeCommand(out, command);

				request = readRequset(inBuffer);
			}
			catch (IOException e)
			{
				closeShell();
				throw new RuntimeException("Exec command: " + command + ",error!", e);
			}
		}
		if(request != null)
			Log.i("Shell", request);		
		return request;
	}

	public String runScript(String[] script)
	{
		StringBuffer request = new StringBuffer();
		for (String command : script)
		{
			request.append(runCommand(command));
		}
		return request.toString();
	}

	private static BufferedReader getBufferedReader(InputStream in)
	{
		return new BufferedReader(new InputStreamReader(in));
	}

	private static void writeCommand(OutputStream out, String command) throws IOException
	{
		out.write(command.getBytes());
		out.write("\n".getBytes());
		out.write("echo EOF:\n".getBytes());
		out.flush();
	}

	private static String readRequset(BufferedReader inBuffer) throws IOException
	{
		StringBuffer request = new StringBuffer();
		while (true)
		{
			String newLine = inBuffer.readLine();
			if("EOF:".equals(newLine))
			{
				return request.toString();
			}
			else
			{
				request.append(newLine);
				request.append('\n');
			}
		}

	}

	public class NotFindShellProcess extends RuntimeException
	{
		private static final long serialVersionUID = 1L;
	}
}
